/**
 * floatObj 包含加减乘除四个方法，能确保浮点数运算不丢失精度
 *
 * 我们知道计算机编程语言里浮点数计算会存在精度丢失问题（或称舍入误差），其根本原因是二进制和实现位数限制有些数无法有限表示
 * 以下是十进制小数对应的二进制表示
 *      0.1 >> 0.0001 1001 1001 1001…（1001无限循环）
 *      0.2 >> 0.0011 0011 0011 0011…（0011无限循环）
 * 计算机里每种数据类型的存储是一个有限宽度，比如 JavaScript 使用 64 位存储数字类型，因此超出的会舍去。舍去的部分就是精度丢失的部分。
 *
 * ** method **
 *  add / subtract / multiply /divide
 *
 * ** explame **
 *  0.1 + 0.2 == 0.30000000000000004 （多了 0.00000000000004）
 *  0.2 + 0.4 == 0.6000000000000001  （多了 0.0000000000001）
 *  19.9 * 100 == 1989.9999999999998 （少了 0.0000000000002）
 *
 * floatObj.add(0.1, 0.2) >> 0.3
 * floatObj.multiply(19.9, 100) >> 1990
 *
 */

/*
 * 判断obj是否为一个整数
 */
import { isEmpty } from '@/utils/publicFun';

function isInteger(obj) {
  return Math.floor(obj) === obj;
}

/**
 * 加法运算
 *
 * @param arg1 {number} 运算数1
 * @param arg2 {number} 运算数2
 * @param digits {number} 精度，保留的小数点数，比如 2, 即保留为两位小数
 * @returns {string} 结果字符串
 */
function add(arg1, arg2, digits) {
  let result = changeNum(arg1, arg2);
  return toFixed(result, digits);
}

/**
 * 减法运算
 *
 * @param arg1 运算数1
 * @param arg2 运算数2
 * @param digits {number} 精度，保留的小数点数，比如 2, 即保留为两位小数
 * @returns {string} 结果字符串
 */
function subtract(arg1, arg2, digits) {
  let result = changeNum(arg1, arg2, false);
  return toFixed(result, digits);
}

/**
 * 乘法运算
 *
 * @param arg1 运算数1
 * @param arg2 运算数2
 * @param digits {number} 精度，保留的小数点数，比如 2, 即保留为两位小数
 * @returns {string} 结果字符串
 */
function multiply(arg1, arg2, digits) {
  let m = 0;
  m = add(m, getDecimalLength(arg1));
  m = add(m, getDecimalLength(arg2));
  let result = (getNum(arg1) * getNum(arg2)) / Math.pow(10, m);
  return toFixed(result, digits);
}

/**
 * 除法运算
 *
 * @param arg1 运算数1
 * @param arg2 运算数2
 * @param digits {number} 精度，保留的小数点数，比如 2, 即保留为两位小数
 * @returns {string} 结果字符串
 */
function divide(arg1, arg2, digits) {
  let t1, t2, result;
  t1 = getDecimalLength(arg1);
  t2 = getDecimalLength(arg2);
  if (t1 - t2 > 0) {
    result = getNum(arg1) / getNum(arg2) / Math.pow(10, t1 - t2);
    return toFixed(result, digits);
  } else {
    result = (getNum(arg1) / getNum(arg2)) * Math.pow(10, t2 - t1);
    return toFixed(result, digits);
  }
}

/**
 * 向上取整
 * @param num
 * @param s
 * @returns {string}
 */
function toFixed(num, s) {
  if (isInteger(num)) {
    return num;
  } else {
    return num.toFixed(s);
  }
}

function changeNum(arg1 = '', arg2 = '', isAdd = true) {
  function changeInteger(arg, r, maxR) {
    if (r !== maxR) {
      let addZero = '';
      for (let i = 0; i < maxR - r; i++) {
        addZero += '0';
      }
      arg = Number(arg.toString().replace('.', '') + addZero);
    } else {
      arg = getNum(arg);
    }
    return arg;
  }
  let r1, r2, maxR, m;
  r1 = getDecimalLength(arg1);
  r2 = getDecimalLength(arg2);
  maxR = Math.max(r1, r2);
  arg1 = changeInteger(arg1, r1, maxR);
  arg2 = changeInteger(arg2, r2, maxR);
  m = Math.pow(10, maxR);
  if (isAdd) {
    return (arg1 + arg2) / m;
  } else {
    return (arg1 - arg2) / m;
  }
}

function getDecimalLength(arg = '') {
  try {
    return arg.toString().split('.')[1].length;
  } catch (e) {
    return 0;
  }
}

function getNum(arg = '') {
  if (isEmpty(arg)) {
    return 0;
  }
  return Number(arg.toString().replace('.', ''));
}

export { add, subtract, multiply, divide, toFixed };
